#include "Precompile.h"
#include "EditorState.h"
#include "SMQuadTree.h"

EditorState::EditorState(EngineInterfacePtr engine):IState(engine){
	m_fpsTimer = boost::shared_ptr<boost::timer>(new boost::timer);
}

void EditorState::onAdded(){
	// Create a scene manager
	m_sceneManager = boost::shared_ptr<Graphics::SMQuadTree>(new Graphics::SMQuadTree);

	// Create a camera
	m_camera = boost::shared_ptr<Utility::QuaternionCamera>(new Utility::QuaternionCamera);
	m_camera->setPosition(0,10,40.0f);
	m_camera->yaw(E_PI);

	// Add a gui
	m_gui = Graphics::GraphicsFactory::createGUIManager();
	m_engine->getRenderSystemPtr()->registerGUIManager(m_gui);
	m_engine->getInputSystemPtr()->registerGUIManager(m_gui);
	initializeGUIComponents();

	m_scriptMgr = ScriptManagerPtr(new Core::ScriptManager(m_engine));

	m_engine->getRenderSystemPtr()->registerSceneManager(m_sceneManager);
	m_sceneManager->addEntity(m_camera);

	m_fpsTimer->restart();
	m_frameCount = 0;
}

void EditorState::onRemoved(){
	m_engine->getRenderSystemPtr()->unregisterSceneManager(m_sceneManager);
}

void EditorState::onUpdated(){
	InputSystemPtr input = m_engine->getInputSystemPtr();
	m_gui->showCursor(true);
	m_gui->setInputInjection(true);

	if(input->keyIsPressed(Input::EIK_W)){
		m_camera->moveForward(float(1.0f));
	}
	else if(input->keyIsPressed(Input::EIK_S)){
		m_camera->moveForward(float(-1.0f));
	}

	if(input->keyIsPressed(Input::EIK_A)){
		m_camera->strafe(-float(1.0f));
	}
	else if(input->keyIsPressed(Input::EIK_D)){
		m_camera->strafe(float(1.0f));
	}

	if(input->keyIsPressed(Input::EIK_SPACE)){
		m_camera->moveUp(float(1.0f));
	}

	if(input->keyIsPressed(Input::EIMOUSEBUTTON_1)){
		m_gui->showCursor(false);
		m_gui->setInputInjection(false);

		std::pair<float,float> mouseDelta = m_engine->getInputSystemPtr()->getMouseDeltaMovement();
		m_camera->yaw(mouseDelta.first*(1.0f/180.0f));
		m_camera->pitch(mouseDelta.second*(1.0f/180.0f));
	}

	std::string selectedMesh = m_gui->getSelectedItemFromList("Editor/ModelFrame/LoadedModelsList");
	if(!selectedMesh.empty()){
		selectedMesh = selectedMesh.substr(1,selectedMesh.find_first_of(')')-1);
		int meshId = Utility::StringConverter::parseInt(selectedMesh);
		MeshPtr mesh = boost::static_pointer_cast<Graphics::Mesh>(m_sceneManager->getEntity(meshId));
		Graphics::ModelPtr model = m_engine->getRenderSystemPtr()->getModel(mesh->getModelId());

		if(mesh){
			// Translate
			if(input->keyIsPressed(Input::EIK_UP)){
				mesh->moveForward(m_context->m_dt*15.0f);
			}
			if(input->keyIsPressed(Input::EIK_DOWN)){
				mesh->moveForward(m_context->m_dt*-15.0f);
			}
			if(input->keyIsPressed(Input::EIK_RIGHT)){
				mesh->moveRight(m_context->m_dt*15.0f);
			}
			if(input->keyIsPressed(Input::EIK_LEFT)){
				mesh->moveRight(-m_context->m_dt*15.0f);
			}
			if(input->keyIsPressed(Input::EIK_NUMPAD1)){
				mesh->moveUp(m_context->m_dt*15.0f);
			}
			if(input->keyIsPressed(Input::EIK_NUMPAD3)){
				mesh->moveUp(-m_context->m_dt*15.0f);
			}

			// Rotate
			if(input->keyIsPressed(Input::EIK_NUMPAD8)){
				mesh->rotate(Utility::Math::_x_,-m_context->m_dt*5.0f);
			}
			if(input->keyIsPressed(Input::EIK_NUMPAD2)){
				mesh->rotate(Utility::Math::_x_,m_context->m_dt*5.0f);
			}
			if(input->keyIsPressed(Input::EIK_NUMPAD4)){
				mesh->rotate(Utility::Math::_y_,-m_context->m_dt*5.0f);
			}
			if(input->keyIsPressed(Input::EIK_NUMPAD6)){
				mesh->rotate(Utility::Math::_y_,m_context->m_dt*5.0f);
			}
			if(input->keyIsPressed(Input::EIK_NUMPAD7)){
				mesh->rotate(Utility::Math::_z_,-m_context->m_dt*5.0f);
			}
			if(input->keyIsPressed(Input::EIK_NUMPAD9)){
				mesh->rotate(Utility::Math::_z_,m_context->m_dt*5.0f);
			}
		}

		if(m_meshName.compare(model->getName()) != 0){
			m_gui->clearList("Editor/ModelSpecFrame/Animations");
			m_gui->clearList("Editor/ModelSpecFrame/JointList");
			// Fill animations list
			int controllerId = model->getControllerId();
			if(controllerId != -1){
				AnimationControllerPtr controller = m_engine->getRenderSystemPtr()->getAnimationaManager()->getAnimationController(controllerId);
				std::vector<std::string> clipNames = controller->getClips();
				std::vector<std::string> jointNames = controller->getJointNames();

				for(unsigned int i=0; i<clipNames.size(); i++){
					m_gui->addListBoxItem("Editor/ModelSpecFrame/Animations",clipNames[i]);
				}
				for(unsigned int i=0; i<jointNames.size(); i++){
					m_gui->addListBoxItem("Editor/ModelSpecFrame/JointList",jointNames[i]);
				}
			}
		}

		std::string selectedAnimation = m_gui->getSelectedItemFromList("Editor/ModelSpecFrame/Animations");
		if(!selectedAnimation.empty() && m_meshName.compare(model->getName()) == 0){
			mesh->playAnimation(selectedAnimation,true,m_context.get());
		}
		else{
			mesh->stopAnimations();
		}

		bool visible = m_gui->getVisible("Editor/ModelSpecFrame");
		if(!visible){
			m_gui->setVisible("Editor/ModelSpecFrame",true);
		}

		m_meshName = model->getName();
	}
	else{
		bool visible = m_gui->getVisible("Editor/ModelSpecFrame");
		if(visible){
			m_gui->setVisible("Editor/ModelSpecFrame",false);
		}
	}

	if(input->keyIsPressed(Input::EIK_DELETE)){
		std::string selectedMesh = m_gui->getSelectedItemFromList("Editor/ModelFrame/LoadedModelsList");
		if(!selectedMesh.empty()){
			m_gui->removeListBoxItem("Editor/ModelFrame/LoadedModelsList",selectedMesh);
			selectedMesh = selectedMesh.substr(1,selectedMesh.find_first_of(')')-1);
			int meshId = Utility::StringConverter::parseInt(selectedMesh);

			m_sceneManager->removeEntity(meshId);
		}
	}

	m_frameCount++;
	if(m_fpsTimer->elapsed() >= 1.0){
		m_gui->setText("Editor/InfoText","FPS: " + Utility::StringConverter::toString(m_frameCount));
		m_frameCount = 0;
		m_fpsTimer->restart();
	}
}

void EditorState::initializeGUIComponents(){
	// Load models list
	std::vector<std::string> models = Core::GeneralSettings::getFilesInFolder(std::string("Models/"));
	for(unsigned int i=0; i<models.size(); i++){
		int point1 = models[i].find_last_of('/')+1;
		int point2 = models[i].find_last_of('.')-point1;
		models[i] = models[i].substr(point1,point2);
		m_gui->addListBoxItem("Editor/ModelFrame/ModelsList",models[i]);
	}

	// Load scriptlist
	std::vector<std::string> scripts = Core::GeneralSettings::getFilesInFolder(std::string("Scripts/"));
	for(unsigned int i=0; i<scripts.size(); i++){
		int point1 = scripts[i].find_last_of('/')+1;
		int point2 = scripts[i].find_last_of('.')-point1;
		scripts[i] = scripts[i].substr(point1,point2);
		m_gui->addListBoxItem("Editor/ScriptFrame/ScriptList",scripts[i]);
	}

	// Register button events
	m_gui->subscribeToButtonClick("Editor/ScriptFrame/LoadButton",boost::function<void(void)>(boost::bind(&EditorState::onLoadScriptButton,this)));
	m_gui->subscribeToButtonClick("Editor/ScriptFrame/UnloadScript",boost::function<void(void)>(boost::bind(&EditorState::onUnloadScriptButton,this)));
	m_gui->subscribeToButtonClick("Editor/ModelFrame/AddModelButton",boost::function<void(void)>(boost::bind(&EditorState::onAddModelButton,this)));
}

void EditorState::onLoadScriptButton(){
	std::string script = m_gui->getSelectedItemFromList("Editor/ScriptFrame/ScriptList");

	if(!script.empty()){
		m_gui->addListBoxItem("Editor/ScriptFrame/LoadedScriptsList",script);
		m_gui->removeListBoxItem("Editor/ScriptFrame/ScriptList",script);

		script += ".lua";
		m_scriptMgr->loadScriptFile(script);
	}
}

void EditorState::onUnloadScriptButton(){
	std::string script = m_gui->getSelectedItemFromList("Editor/ScriptFrame/LoadedScriptsList");

	if(!script.empty()){
		m_gui->removeListBoxItem("Editor/ScriptFrame/LoadedScriptsList",script);
		m_gui->addListBoxItem("Editor/ScriptFrame/ScriptList",script);

		script += ".lua";
		m_scriptMgr->unloadScriptFile(script);
	}
}

void EditorState::onAddModelButton(){
	std::string model = m_gui->getSelectedItemFromList("Editor/ModelFrame/ModelsList");
	if(!model.empty()){
		MeshPtr mesh = MeshPtr(new Graphics::Mesh(model+".bin"));
		int entityId = m_sceneManager->addEntity(mesh);

		std::string listItemName = "("+Utility::StringConverter::toString(entityId)+")"+model;
		m_gui->addListBoxItem("Editor/ModelFrame/LoadedModelsList",listItemName);
	}
}